/* Copyright (c) 2020 XEPIC Corporation Limited */
#ifndef IVL_Statement_H
#define IVL_Statement_H
/*
 * Copyright (c) 1998-2019 Stephen Williams (steve@icarus.com)
 *
 *    This source code is free software; you can redistribute it
 *    and/or modify it in source code form under the terms of the GNU
 *    General Public License as published by the Free Software
 *    Foundation; either version 2 of the License, or (at your option)
 *    any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 * USA.
 */

#include <list>
#include <string>
#include <vector>

#include "HName.h"
#include "LineInfo.h"
#include "PDelays.h"
#include "PExpr.h"
#include "PScope.h"
#include "StringHeap.h"
#include "ivl_target.h"
#include "svector.h"
class PExpr;
class PChainConstructor;
class PPackage;
class Statement;
class PEventStatement;
class Design;
class NetAssign_;
class NetCAssign;
class NetDeassign;
class NetForce;
class NetScope;

/*
 * The PProcess is the root of a behavioral process. Each process gets
 * one of these, which contains its type (initial, always, or final)
 * and a pointer to the single statement that is the process. A module
 * may have several concurrent processes.
 */
class PProcess : public LineInfo {
 public:
  PProcess(ivl_process_type_t t, Statement* st) : type_(t), statement_(st) {}

  virtual ~PProcess();

  bool elaborate(Design* des, NetScope* scope) const;

  ivl_process_type_t type() const { return type_; }
  Statement* statement() { return statement_; }

  map<perm_string, PExpr*> attributes;

  virtual void dump(ostream& out, unsigned ind) const;

 private:
  ivl_process_type_t type_;
  Statement* statement_;
};

/*
 * The PProcess is a process, the Statement is the actual action. In
 * fact, the Statement class is abstract and represents all the
 * possible kinds of statements that exist in Verilog.
 */
class Statement : virtual public LineInfo {
 public:
  Statement() {}
  virtual ~Statement() = 0;

  virtual void dump(ostream& out, unsigned ind) const;
  virtual NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void elaborate_scope(Design* des, NetScope* scope) const;
  virtual void elaborate_sig(Design* des, NetScope* scope) const;

  map<perm_string, PExpr*> attributes;
};

/*
 * Assignment statements of the various forms are handled by this
 * type. The rvalue is an expression. The lvalue needs to be figured
 * out by the parser as much as possible.
 */
class PAssign_ : public Statement {
 public:
  explicit PAssign_(PExpr* lval, PExpr* ex, bool is_constant);
  explicit PAssign_(PExpr* lval, PExpr* de, PExpr* ex);
  explicit PAssign_(PExpr* lval, PExpr* cnt, PEventStatement* de, PExpr* ex);
  virtual ~PAssign_() = 0;

  const PExpr* lval() const { return lval_; }
  PExpr* rval() const { return rval_; }

 protected:
  NetAssign_* elaborate_lval(Design*, NetScope* scope) const;
  NetExpr* elaborate_rval_(Design*, NetScope*, ivl_type_t lv_net_type,
                           ivl_variable_type_t lv_type, unsigned lv_width,
                           bool force_unsigned = false) const;
  NetExpr* elaborate_rval_(Design*, NetScope*, ivl_type_t ntype) const;

  NetExpr* elaborate_rval_obj_(Design*, NetScope*,
                               ivl_variable_type_t type) const;

  PExpr* delay_;
  PEventStatement* event_;
  PExpr* count_;

 private:
  PExpr* lval_;
  PExpr* rval_;
  bool is_constant_;
};

class PAssign : public PAssign_ {
 public:
  // lval  - assignment l-value
  // ex    - assignment r-value
  // op    - compressed assignment operator (i.e. '+', '-', ...)
  // de    - delayed assignment delay expression
  explicit PAssign(PExpr* lval, PExpr* ex);
  explicit PAssign(PExpr* lval, char op, PExpr* ex);
  explicit PAssign(PExpr* lval, PExpr* de, PExpr* ex);
  explicit PAssign(PExpr* lval, PExpr* cnt, PEventStatement* de, PExpr* ex);
  explicit PAssign(PExpr* lval, PExpr* ex, bool is_constant);
  ~PAssign();

  virtual void dump(ostream& out, unsigned ind) const;
  virtual NetProc* elaborate(Design* des, NetScope* scope) const;

 private:
  NetProc* elaborate_compressed_(Design* des, NetScope* scope) const;
  char op_;
};

class PAssignNB : public PAssign_ {
 public:
  explicit PAssignNB(PExpr* lval, PExpr* ex);
  explicit PAssignNB(PExpr* lval, PExpr* de, PExpr* ex);
  explicit PAssignNB(PExpr* lval, PExpr* cnt, PEventStatement* de, PExpr* ex);
  ~PAssignNB();

  virtual void dump(ostream& out, unsigned ind) const;
  virtual NetProc* elaborate(Design* des, NetScope* scope) const;

 private:
  NetProc* assign_to_memory_(class NetMemory*, PExpr*, Design* des,
                             NetScope* scope) const;
};

/*
 * A block statement is an ordered list of statements that make up the
 * block. The block can be sequential or parallel, which only affects
 * how the block is interpreted. The parser collects the list of
 * statements before constructing this object, so it knows a priori
 * what is contained.
 */
class PBlock : public PScope, public Statement, public PNamedItem {
 public:
  enum BL_TYPE { BL_SEQ, BL_PAR, BL_JOIN_NONE, BL_JOIN_ANY };

  // If the block has a name, it is a scope and also has a parent.
  explicit PBlock(perm_string n, LexicalScope* parent, BL_TYPE t);
  // If it doesn't have a name, it's not a scope
  explicit PBlock(BL_TYPE t);
  ~PBlock();

  BL_TYPE bl_type() const { return bl_type_; }

  bool var_init_needs_explicit_lifetime() const;

  // This is only used if this block is the statement list for a
  // constructor. We look for a PChainConstructor as the first
  // statement, and if it is there, extract it.
  PChainConstructor* extract_chain_constructor();

  // If the bl_type() is BL_PAR, it is possible to replace it
  // with JOIN_NONE or JOIN_ANY. This is to help the parser.
  void set_join_type(BL_TYPE);

  void set_statement(const std::vector<Statement*>& st);

  // Copy the statement from that block to the front of this
  // block.
  void push_statement_front(Statement* that);

  virtual void dump(ostream& out, unsigned ind) const;
  virtual NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void elaborate_scope(Design* des, NetScope* scope) const;
  virtual void elaborate_sig(Design* des, NetScope* scope) const;

  SymbolType symbol_type() const;

 private:
  BL_TYPE bl_type_;
  std::vector<Statement*> list_;
};

class PCallTask : public Statement {
 public:
  explicit PCallTask(PPackage* pkg, const pform_name_t& n,
                     const list<PExpr*>& parms);
  explicit PCallTask(const pform_name_t& n, const list<PExpr*>& parms);
  explicit PCallTask(perm_string n, const list<PExpr*>& parms);
  ~PCallTask();

  const pform_name_t& path() const;

  virtual void dump(ostream& out, unsigned ind) const;
  virtual NetProc* elaborate(Design* des, NetScope* scope) const;

 private:
  NetProc* elaborate_sys(Design* des, NetScope* scope) const;
  NetProc* elaborate_usr(Design* des, NetScope* scope) const;

  NetProc* elaborate_method_(Design* des, NetScope* scope,
                             bool add_this_flag = false) const;
  NetProc* elaborate_function_(Design* des, NetScope* scope) const;
  NetProc* elaborate_void_function_(Design* des, NetScope* scope,
                                    NetFuncDef* def) const;

  NetProc* elaborate_build_call_(Design* des, NetScope* scope, NetScope* task,
                                 NetExpr* use_this) const;
  NetProc* elaborate_sys_task_method_(Design* des, NetScope* scope, NetNet* net,
                                      perm_string method_name,
                                      const char* sys_task_name) const;
  bool test_task_calls_ok_(Design* des, NetScope* scope) const;

  PPackage* package_;
  pform_name_t path_;
  vector<PExpr*> parms_;
};

class PCase : public Statement {
 public:
  struct Item {
    list<PExpr*> expr;
    Statement* stat;
  };

  PCase(ivl_case_quality_t, NetCase::TYPE, PExpr* ex, svector<Item*>*);
  ~PCase();

  virtual NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void elaborate_scope(Design* des, NetScope* scope) const;
  virtual void elaborate_sig(Design* des, NetScope* scope) const;
  virtual void dump(ostream& out, unsigned ind) const;

 private:
  ivl_case_quality_t quality_;
  NetCase::TYPE type_;
  PExpr* expr_;

  svector<Item*>* items_;

 private:  // not implemented
  PCase(const PCase&);
  PCase& operator=(const PCase&);
};

class PCAssign : public Statement {
 public:
  explicit PCAssign(PExpr* l, PExpr* r);
  ~PCAssign();

  virtual NetCAssign* elaborate(Design* des, NetScope* scope) const;
  virtual void dump(ostream& out, unsigned ind) const;

 private:
  PExpr* lval_;
  PExpr* expr_;
};

/*
 * This represents the syntax "super.new(...)". This is not really an
 * executable statement, but the elaborator will handle these
 * specially and will remove them from the statement stream. If any
 */
class PChainConstructor : public Statement {
 public:
  explicit PChainConstructor(const list<PExpr*>& parms);
  ~PChainConstructor();

  virtual NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void dump(ostream& out, unsigned ind) const;

  inline const std::vector<PExpr*>& chain_args(void) const { return parms_; }

 private:
  std::vector<PExpr*> parms_;
};

class PCondit : public Statement {
 public:
  PCondit(PExpr* ex, Statement* i, Statement* e);
  ~PCondit();

  virtual NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void elaborate_scope(Design* des, NetScope* scope) const;
  virtual void elaborate_sig(Design* des, NetScope* scope) const;
  virtual void dump(ostream& out, unsigned ind) const;

 private:
  PExpr* expr_;
  Statement* if_;
  Statement* else_;

 private:  // not implemented
  PCondit(const PCondit&);
  PCondit& operator=(const PCondit&);
};

class PDeassign : public Statement {
 public:
  explicit PDeassign(PExpr* l);
  ~PDeassign();

  virtual NetDeassign* elaborate(Design* des, NetScope* scope) const;
  virtual void dump(ostream& out, unsigned ind) const;

 private:
  PExpr* lval_;
};

class PDelayStatement : public Statement {
 public:
  PDelayStatement(PExpr* d, Statement* st);
  ~PDelayStatement();

  virtual void dump(ostream& out, unsigned ind) const;
  virtual NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void elaborate_scope(Design* des, NetScope* scope) const;
  virtual void elaborate_sig(Design* des, NetScope* scope) const;

 private:
  PExpr* delay_;
  Statement* statement_;
};

/*
 * This represents the parsing of a disable <scope> statement.
 */
class PDisable : public Statement {
 public:
  explicit PDisable(const pform_name_t& sc);
  ~PDisable();

  virtual void dump(ostream& out, unsigned ind) const;
  virtual NetProc* elaborate(Design* des, NetScope* scope) const;

 private:
  pform_name_t scope_;
};

class PDoWhile : public Statement {
 public:
  PDoWhile(PExpr* ex, Statement* st);
  ~PDoWhile();

  virtual NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void elaborate_scope(Design* des, NetScope* scope) const;
  virtual void elaborate_sig(Design* des, NetScope* scope) const;
  virtual void dump(ostream& out, unsigned ind) const;

 private:
  PExpr* cond_;
  Statement* statement_;
};

/*
 * The event statement represents the event delay in behavioral
 * code. It comes from such things as:
 *
 *      @name <statement>;
 *      @(expr) <statement>;
 *      @* <statement>;
 */
class PEventStatement : public Statement {
 public:
  explicit PEventStatement(const svector<PEEvent*>& ee);
  explicit PEventStatement(PEEvent* ee);
  // Make an @* statement or make a special @* version with the items
  // from functions added and outputs removed for always_comb/latch.
  explicit PEventStatement(bool always_sens = false);

  ~PEventStatement();

  void set_statement(Statement* st);

  virtual void dump(ostream& out, unsigned ind) const;
  // Call this with a NULL statement only. It is used to print
  // the event expression for inter-assignment event controls.
  virtual void dump_inline(ostream& out) const;
  virtual NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void elaborate_scope(Design* des, NetScope* scope) const;
  virtual void elaborate_sig(Design* des, NetScope* scope) const;

  bool has_aa_term(Design* des, NetScope* scope);

  // This method is used to elaborate, but attach a previously
  // elaborated statement to the event.
  NetProc* elaborate_st(Design* des, NetScope* scope, NetProc* st) const;

  NetProc* elaborate_wait(Design* des, NetScope* scope, NetProc* st) const;
  NetProc* elaborate_wait_fork(Design* des, NetScope* scope) const;

 private:
  svector<PEEvent*> expr_;
  Statement* statement_;
  bool always_sens_;
};

ostream& operator<<(ostream& o, const PEventStatement& obj);

class PForce : public Statement {
 public:
  explicit PForce(PExpr* l, PExpr* r);
  ~PForce();

  virtual NetForce* elaborate(Design* des, NetScope* scope) const;
  virtual void dump(ostream& out, unsigned ind) const;

 private:
  PExpr* lval_;
  PExpr* expr_;
};

class PForeach : public Statement {
 public:
  explicit PForeach(perm_string var, const std::list<perm_string>& ix,
                    Statement* stmt);
  ~PForeach();

  virtual NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void elaborate_scope(Design* des, NetScope* scope) const;
  virtual void elaborate_sig(Design* des, NetScope* scope) const;
  virtual void dump(ostream& out, unsigned ind) const;

 private:
  NetProc* elaborate_static_array_(Design* des, NetScope* scope,
                                   const std::vector<netrange_t>& dims) const;

 private:
  perm_string array_var_;
  std::vector<perm_string> index_vars_;
  Statement* statement_;
};

class PForever : public Statement {
 public:
  explicit PForever(Statement* s);
  ~PForever();

  virtual NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void elaborate_scope(Design* des, NetScope* scope) const;
  virtual void elaborate_sig(Design* des, NetScope* scope) const;
  virtual void dump(ostream& out, unsigned ind) const;

 private:
  Statement* statement_;
};

class PForStatement : public Statement {
 public:
  PForStatement(PExpr* n1, PExpr* e1, PExpr* cond, Statement* step,
                Statement* body);
  ~PForStatement();

  virtual NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void elaborate_scope(Design* des, NetScope* scope) const;
  virtual void elaborate_sig(Design* des, NetScope* scope) const;
  virtual void dump(ostream& out, unsigned ind) const;

 private:
  PExpr* name1_;
  PExpr* expr1_;

  PExpr* cond_;

  Statement* step_;

  Statement* statement_;
};

class PNoop : public Statement {
 public:
  PNoop() {}
  ~PNoop() {}
};

class PRepeat : public Statement {
 public:
  explicit PRepeat(PExpr* expr, Statement* s);
  ~PRepeat();

  virtual NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void elaborate_scope(Design* des, NetScope* scope) const;
  virtual void elaborate_sig(Design* des, NetScope* scope) const;
  virtual void dump(ostream& out, unsigned ind) const;

 private:
  PExpr* expr_;
  Statement* statement_;
};

class PRelease : public Statement {
 public:
  explicit PRelease(PExpr* l);
  ~PRelease();

  virtual NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void dump(ostream& out, unsigned ind) const;

 private:
  PExpr* lval_;
};

class PReturn : public Statement {
 public:
  explicit PReturn(PExpr* e);
  ~PReturn();

  NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void dump(std::ostream& out, unsigned ind) const;

 private:
  PExpr* expr_;
};

/*
 * The PTrigger statement sends a trigger to a named event. Take the
 * name here.
 */
class PTrigger : public Statement {
 public:
  explicit PTrigger(PPackage* pkg, const pform_name_t& ev);
  ~PTrigger();

  virtual NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void dump(ostream& out, unsigned ind) const;

 private:
  PPackage* package_;
  pform_name_t event_;
};

class PWhile : public Statement {
 public:
  PWhile(PExpr* ex, Statement* st);
  ~PWhile();

  virtual NetProc* elaborate(Design* des, NetScope* scope) const;
  virtual void elaborate_scope(Design* des, NetScope* scope) const;
  virtual void elaborate_sig(Design* des, NetScope* scope) const;
  virtual void dump(ostream& out, unsigned ind) const;

 private:
  PExpr* cond_;
  Statement* statement_;
};

#endif /* IVL_Statement_H */
